#include "TypeManager.h"
#include "LineItem.h"
#include "TypeEntryItem.h"
#include "TypeEntryFrameItem.h"
#include "NodeItem.h"
#include "AbstractMappingWindow.h"
#include "TypeVariableEntry.h"
#include "MappingEngine.h"
#include "Command_Agent.h"
#include "xNodeType_Math.h"
#include "xMappingTree.h"
#include "xInMappingPort.h"
#include "TypeVariableEntry.h"
#include "NodeInPort.h"
#include "NodeOutPort.h"
#include "WOPM.h"
#include "DiagramScene.h"
#include "TypeDialog.h"
#include "TypeVariablePanel.h"
#include "WOPM.h"
#include "xMappingNodeTypeLibrary.h"

#include <QPen>
#include <QPushButton>
#include <QVBoxLayout>
#include <QPointF>

TypeManager::TypeManager( AbstractMappingWindow* mappingDialog)
	:AbstractManager(mappingDialog)
{
	this->_mpnEntry = NULL;
	this->_root = NULL;
}

TypeManager::~TypeManager()
{
	delete _mpnEntry;
	delete _root;
}

// 呼叫NodeEntry::addEntryToList，把entry加入list中
void TypeManager::createEntry( WOP_Entry* entry, bool canUnfold )
{
	assert( entry != NULL );
	if( _mpnEntry == NULL )
		_mpnEntry = new TypeVariableEntry();

	_mpnEntry->addEntryToList( entry,this->xmt_, canUnfold );
}
void TypeManager::createArrEntry( WOP_Entry* entry ){
	this->createEntry(entry,true);
}
void TypeManager::createPtvEntry( WOP_Entry* entry ){
	this->createEntry(entry,false);
}
void TypeManager::createRefEntry( WOP_Entry* entry ){
	this->createEntry(entry,true);
}


TypeVariableEntry* TypeManager::getEntry()
{
	return this->_mpnEntry;
}

TypeEntryItem* TypeManager::createEntryItem( const string & entryName, const string & entryType, xMappingNode* entryNode, WOP_Entry* wopEntry, TypeEntryFrameItem* frameItem)
{
	TypeEntryItem * entryShape = new TypeEntryItem( entryName, entryType, entryNode, wopEntry, this, this->iPanel_, frameItem);
	this->dialog_->connect( entryShape, SIGNAL( contextMenuRequired( WOP_Entry *, NodeOutPort * ) ), SLOT( onContextMenu( WOP_Entry *, NodeOutPort * ) ) );
	this->nodeList_.push_back(entryShape);
	entryShape->setZValue(1);
	return entryShape;	
}

void TypeManager::initVariableEntry( WOP_Entry* entry )
{
	assert(entry!=NULL);

	if ( entry->getTypeStr() == "ref") {
		this->createPtvEntry(entry);
	} else if ( entry->getTypeStr() == "array" ) {
		this->unfoldEntry(entry);				// 如果entry type是array, 則直接將它展開
	} else if ( entry->getTypeStr() == "object" ){
		this->createPtvEntry(entry);
	} else {
		this->createPtvEntry(entry);
	}
}


void TypeManager::unfoldEntry( WOP_Entry* entry )
{

	//將子entry一個一個加入NodeEntry的list中
	vector<WOP_Entry*> childVector=*(entry->getChildsPtr());
	vector<WOP_Entry*>::iterator itr;

	for(itr=childVector.begin();itr!=childVector.end();itr++){
		string enType=(*itr)->getTypeStr();
		if((enType=="ref")||(enType=="object")||(enType=="array"))
			this->createEntry(*itr,true);
		else
			this->createEntry(*itr,false);
	}
}

void TypeManager::createObjEntry( WOP_Entry* entry)
{
	WOP_Obj_Entry* objEntry=(WOP_Obj_Entry*)entry;
	vector<WOP_Entry*> childVector=objEntry->getChildren();
	vector<WOP_Entry*>::iterator itr;

	for(itr=childVector.begin();itr!=childVector.end();itr++){
		string enType=(*itr)->getTypeStr();
		if((enType=="ref")||(enType=="object")||(enType=="array"))
			this->createEntry(*itr,true);
		else
			this->createEntry(*itr,false);
	}
}

NodeItem* TypeManager::getRootNode()
{
	return this->_root;
}

TypeEntryItem* TypeManager::findEntryItemByName( const string & entryName )
{
	for ( int i = 0; i < this->nodeList_.size(); ++i )
	{
		if ( this->nodeList_.at(i)->nodeType() == AbstractShapeItem::ENTRY_NODE ){
			TypeEntryItem* en = dynamic_cast<TypeEntryItem*>(this->nodeList_.at(i));
			assert(en);
			if ( en->getWOPEntry()->getName() == entryName ) {
				return en;
			}
		}
	}
	return NULL;
}

// 將mapping tree存成mds檔案
void TypeManager::saveMapping( QString fileName, WOP_Entry* en )
{
	MappingEngine::getInstance()->saveToMDSfile(fileName.toStdString(), en->getVarType(), this->xmt_->toMDString());
}

void TypeManager::setSaveFileName( const QString & fileName )
{
	this->_saveFileName = fileName;
}
const QString & TypeManager::getSaveFileName() const
{
	return this->_saveFileName;
}

//在diva的Mapping Engine裡面新增一筆MDSString，但並不存檔，由apply all觸發
void TypeManager::addMDSString( WOP_Entry * entry )
{
	auto typeName = entry->getVarType();
	auto mds = this->xmt_->toMDString();
	MappingEngine::getInstance()->addMDString( typeName, mds );
//	xMappingNodeTypeLibrary::getInstance()->add_typemapping_from_xMappingTree( typeName, "", this->xmt_ );

	// visualize this vm immediately
	MappingEngine::getInstance()->xCreateMappedObject( this->xmt_ );
	MappingEngine::getInstance()->add_wop_xmt_pair( entry, this->xmt_ );
	MappingEngine::getInstance()->addxMappingTree( this->xmt_ ); // add all the nodes to be managed by mapping engine
}

//delete empty port of wop collector and vm collector
//刪除所有在這個dialog中的空port，(僅對wop collector and vm collector有效)
void TypeManager::deleteEmptyPort(){
	for(auto itr=this->nodeList_.begin();itr!=this->nodeList_.end();itr++){
		if ((*itr)->nodeType() == AbstractShapeItem::MAPPING_NODE){
			(*itr)->deleteEmptyInPort();
			(*itr)->deleteEmptyOutPort();
		}
	}
}

// 尋找 mapping 中的 root node，如果找到唯一一個則回傳 node
// 如果找到大於一個 root node 或沒有找到 root node 皆回傳 NULL
NodeItem* TypeManager::getRoot()
{
	NodeItem* root = NULL;
	auto firstLinks = this->getVariablePanel()->getItems().front()->getOutportList().front()->getPortLineList();
	for( auto it = firstLinks.begin(); it != firstLinks.end(); ++it ) {
		if( ( *it )->getInport()->getInport()->getType() == "varname" ) {
			root = dynamic_cast< NodeItem * >( ( *it )->getInport()->getParent() );
		}
	}
	return root;
}

// 檢查 root node 是否唯一
bool TypeManager::checkRootNumberValidate()
{
	NodeItem* root = this->getRoot();
	if ( root == NULL ){
		return false;
	} else {
		return true;
	}
}


void TypeManager::deleteEntry()
{
	delete _mpnEntry;
	_mpnEntry = NULL;
}

void TypeManager::reset()
{
	deleteEntry();
	this->nodeList_.clear();
	this->iPanel_ = nullptr;
	this->xmt_ = new xMappingTree();
}


AbstractPanel* TypeManager::createInputPanel()
{
	this->iPanel_ = new TypeVariablePanel(this);
	this->inputPanel_ = this->iPanel_;
	return this->inputPanel_;
}

AbstractPanel * TypeManager::createOutputPanel() {
	return nullptr;
}

TypeVariablePanel* TypeManager::getVariablePanel()
{
	return this->iPanel_;
}

std::vector<TypeEntryItem*> TypeManager::createUnfoldEntries(WOP_Entry *entry){

	std::vector<TypeEntryItem*> children;
	int index = getEntry()->getEntryList().size();
	
	// 如果此entry是reference entry，且子entry尚未建立的話，則詢問command agent
	std::string parType=entry->getTypeStr();
	std::string val = entry->getVal();
	if( parType == "ref" && ( val != "null" && val != "0x0" ) ) {
	
		entry = Command_Agent::getInstance()->unfoldRefPure(entry);

		// 由於unfoldRefPure會將entry加進table中，DIVA會檢查table裡面entry跟VM的配對
		// 但是這時候entry根本就還沒有做任何mapping，會導致錯誤，所以這裡會先把entry從table中remove掉
		WOPM::getInstance()->removeEntry(entry);

		this->createEntry( entry, true );
		auto unfoldItem = this->createEntryItem( entry->getName(), entry->getTypeStr(), this->getEntry()->getEntryList().back(), this->getEntry()->getWopList().back() );
		children.push_back( unfoldItem );
	} else if( parType == "object" || parType == "array" ) {

		// 詢問完畢後，將子entry一個一個加入NodeEntry的list中
		vector<WOP_Entry*> childVector = *( entry->getChildsPtr() );

		for( auto itr = childVector.begin(); itr != childVector.end(); itr++ ) {
			string enType=(*itr)->getTypeStr();
			if((enType=="ref")||(enType=="object")||(enType=="array"))
				this->createEntry(*itr,true);
			else
				this->createEntry(*itr,false);
		}

		// 建立unfold出來的entry items，並加入childeren vector內
		std::vector<WOP_Entry*> wopList = getEntry()->getWopList();
		std::vector<xMappingNode*> entryList = getEntry()->getEntryList();

		for ( int i = index; i < entryList.size(); ++i )
		{
			TypeEntryItem * unfoldItem = this->createEntryItem( getEntry()->getEntryNameAT(i), getEntry()->getEntryTypeAT(i),entryList.at(i), wopList.at(i) );
			children.push_back(unfoldItem);
		}
	}
	return children;
}

void TypeManager::createEntryFrameItem( WOP_Entry* entry, std::vector<xMappingNode*> &entryList, DiagramScene* scene, AbstractPanel* panel )
{
	TypeEntryFrameItem* frame = new TypeEntryFrameItem( entry, entryList, scene, panel);
	this->dialog_->connect( frame, SIGNAL( contextMenuRequired( WOP_Entry * ) ), SLOT( onContextMenu( WOP_Entry * ) ) );
	this->nodeList_.push_back(frame);
}

/*
NodeItem * TypeManager::createNode( WOP_Entry * en ) {
	NodeItem* node = new NodeItem( en, this->xmt_, this );
	connect(node, SIGNAL(itemSelected(NodeItem* )), this, SIGNAL(itemSelected(NodeItem*)));
	connect(node, SIGNAL(portListChanged(NodeItem* )), this, SIGNAL(itemSelected(NodeItem*)));
	connect(node, SIGNAL(itemDeSelected()), this, SIGNAL(itemDeSelected()));
	this->nodeList_.push_back(node);
	return node;
}
*/

void TypeManager::saveVMtoDESFiles( const QString & fileName, const QString & type, const QString & description )
{
	std::string name = this->_mpnEntry->getWopList().front()->getVarType();

	std::string desString = getMappingTree()->toTMString( name, type.toStdString(), description.toStdString() );

	//製作新的des file
	std::ofstream desfile( fileName.toStdString().c_str(), std::ios_base::out | std::ios_base::trunc);
	desfile << desString << std::endl ;
	desfile.close();
}
